package ai.neuralnet.gradientDescent;

import ai.neuralnet.TNeuron;

public class GDNeuron extends TNeuron<GDNeuron>
{

    private double learningRate;

    private double sigma;

    private boolean sigmaSet;

    private double[] dw;

    private boolean allOutputsReady()
    {
        boolean ready = true;
        for (GDNeuron n : outputs)
        {
            ready &= n.sigmaSet;
        }

        return ready;
    }

    public void tryPropagateBackward()
    {
        if (!allOutputsReady()) return;


        double sum = 0;
        for (GDNeuron n : outputs)
        {
            sum += n.weights[n.getInputIndex(this)] * n.sigma;
        }

        double output = computeOutput();

        sigma = output * (1 - output) * sum;
        sigmaSet = true;

    }

    void resetDeltaWeights()
    {
        for (int i = 0; i < dw.length; i++)
        {
            dw[i] = 0;
        }
    }

    void updateDeltaWeights()
    {

        dw[0] += sigma * biasInput;

        for (int i = 1; i < weights.length; i++)
        {
            dw[i] += sigma * inputs[i - 1];
        }
    }


    void updateWeights()
    {
        for (int i = 0; i < weights.length; i++)
        {
            weights[i] += learningRate * dw[i] / inputs.length;
        }
    }

    public void computeOutputSigma(double targetValue)
    {
        double output = computeOutput();
        sigma = (targetValue - output) * output * (1 - output);
        sigmaSet = true;
    }

    public void resetSigmaSet()
    {
        sigmaSet = false;
    }


    public GDNeuron(int inputCount)
    {
        super(inputCount);
        dw = new double[inputCount + 1];
    }


    public boolean isSigmaSet()
    {
        return sigmaSet;
    }

    public void setLearningRate(double learningRate)
    {
        this.learningRate = learningRate;
    }

    public double getLearningRate()
    {
        return learningRate;
    }

}
